package com.zha.config;

import com.alibaba.fastjson.JSONObject;
import com.zha.constant.AuthConstants;
import com.zha.constant.BusinessEnum;
import com.zha.model.LoginResult;
import com.zha.model.Result;
import com.zha.model.SecurityUser;
import com.zha.service.impl.UserDetailsServiceImpl;
import com.zha.util.JSONUtils;
import com.zha.util.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.util.UUID;
import java.util.concurrent.TimeUnit;


/**
 * @Title: 只有光头才能变强
 * @Author: 炸炸
 * @Date: 2024/12/7 17:21
 * Security安全框架配置类
 */
@Configuration
@Slf4j
@EnableWebSecurity
public class AuthSecurityConfig {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 告诉security走自己的认证流程
     */
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    /**
     * 设置security相关http设置
     */
   /* @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 关闭跨站请求
        http.cors().disable();
        // 关闭跨域请求
        http.csrf().disable();
        // 关闭session使用策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 配置登录相关信息
        http.formLogin()
                .loginProcessingUrl(AuthConstants.LOGIN_URL) // 指定登录的路径
                .successHandler(authenticationSuccessHandler())   // 配置登录成功处理器
                .failureHandler(authenticationFailureHandler());  // 配置登录失败处理器
        // 配置登出相关信息
        http.logout()
                .logoutUrl(AuthConstants.LOGOUT_URL)    // 指定登出的路径
                .logoutSuccessHandler(logoutSuccessHandler());  // 登出成功处理器
        // 配置其他请求
        http.authorizeHttpRequests().anyRequest().authenticated();

        return http.build();
    }*/
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                // 关闭跨站请求
                .cors(AbstractHttpConfigurer::disable)
                // 关闭跨域请求
                .csrf(AbstractHttpConfigurer::disable)
                // 关闭session使用策略
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 配置登录相关信息
                .formLogin(formLogin -> formLogin
                        .loginProcessingUrl(AuthConstants.LOGIN_URL) // 指定登录的路径
                        .successHandler(authenticationSuccessHandler()) // 配置登录成功处理器
                        .failureHandler(authenticationFailureHandler())) // 配置登录失败处理器
                // 配置登出相关信息
                .logout(logout -> logout
                        .logoutUrl(AuthConstants.LOGOUT_URL) // 指定登出的路径
                        .logoutSuccessHandler(logoutSuccessHandler())) // 登出成功处理器
                // 配置其他请求
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated()) // 所有请求都需要认证
                .build();
    }


    /**
     * 登录成功处理器
     */
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return (request, response, authentication) -> {
            // 使用UUID生成一个字符串,来当作我们用户的令牌TOKEN
            String token = UUID.randomUUID().toString();
            // 获取刚刚认证过的安全认证用户对象信息
            SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
            // 将安全认证用户对像securityUser转换为json格式的字符串
            String securityUserJsonStr = JSONObject.toJSONString(securityUser);
            // 将token作为key,安全认证用户对象json格式字符串作为value存放到redis中
            stringRedisTemplate.opsForValue().set(AuthConstants.LOGIN_TOKEN_PREFIX+token, securityUserJsonStr,AuthConstants.TOKEN_TIME, TimeUnit.SECONDS);
            // 响应给前端令牌token和token有限时长
            // 封装一个登录统一结果对象
            LoginResult loginResult = new LoginResult(token,AuthConstants.TOKEN_TIME);
            // 创建一个项目统一返回结果对象
            Result result  = Result.success(loginResult);
            // 响应给前端
            ResponseUtils.write(response, JSONUtils.toJson(result));
        };
    }

    /**
     * 登录失败处理器
     */
    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return (request, response, exception) -> {
            // 封装一个项目统一返回结果对象
            Result result = new Result();
            result.setCode(BusinessEnum.LOGIN_ERROR.getCode());
            // 判断异常类型
            if (exception instanceof BadCredentialsException){
                result.setMessage("用户名或密码错误");
            }else if (exception instanceof UsernameNotFoundException){
                result.setMessage("用户不存在");
            }else if (exception instanceof AccountStatusException){
                result.setMessage("用户状态异常,请联系管理员");
            }else if (exception instanceof InternalAuthenticationServiceException){
                result.setMessage(exception.getMessage());
            }else {
                result.setMessage(BusinessEnum.LOGIN_ERROR.getDesc());
            }
            // 响应给前端
            ResponseUtils.write(response, JSONUtils.toJson(result));
        };
    }

    /**
     * 登出成功处理器
     */
    @Bean
    public LogoutSuccessHandler logoutSuccessHandler(){
        return (request, response, authentication) -> {
            // 处理登出成功逻辑
            // 从请求头中回去Authorization字段的值
            String authorizationValue = request.getHeader(AuthConstants.AUTHORIZATION);   // AUTHORIZATION  令牌TOKEN存放在请求中的KEY
            // 从authorizationValue中获取token
            String token = authorizationValue.replaceFirst(AuthConstants.BEARER, ""); //BEARER 令牌TOKEN存放到请求头中值的前缀
            // 将token从redis中删除
            stringRedisTemplate.delete(AuthConstants.LOGIN_TOKEN_PREFIX+token);
            // 创建项目统一返回结果对象
            Result result = Result.success();

            // 响应给前端
            ResponseUtils.write(response, JSONUtils.toJson(Result.success()));
        };
    }
}
